package com.xxx.mall.filter;

import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xxx.mall.adapter.AuthConfigAdapter;
import com.xxx.mall.bo.AuthAccountBO;
import com.xxx.mall.context.AuthUserContext;
import com.xxx.mall.exception.MallException;
import com.xxx.mall.feign.TokenFeignService;
import com.xxx.mall.response.R;
import com.xxx.mall.response.ResponseEnum;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

/**
 * @author 邢晨旭
 * {@code @date} 2023/11/16
 */
@Component
public class AuthFilter extends HttpFilter {

    public static final String FEIGN_INSIDE_URL_PREFIX = "/feign";

    @Resource
    AuthConfigAdapter authConfigAdapter;

    @Resource
    TokenFeignService tokenFeignService;

    @Resource
    ObjectMapper objectMapper;

    @Override
    protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        //feign调用时，暂时不进行拦截
        if (feignRequestCheck(request)) {
            chain.doFilter(request, response);
            return;
        }
        //请求白名单，不进行拦截
        List<String> excludePathPatterns = authConfigAdapter.excludePathPatterns();
        if (!CollectionUtils.isEmpty(excludePathPatterns)) {
            for (String excludePathPattern : excludePathPatterns) {
                if (new AntPathMatcher().match(excludePathPattern, request.getRequestURI())) {
                    chain.doFilter(request, response);
                    return;
                }
            }
        }
        //校验
        String accessToken = request.getHeader("Authorization");
        if (StrUtil.isBlank(accessToken)) {
            //未登录
            printServerResponseToWeb(response, R.err(ResponseEnum.UNAUTHORIZED));
            return;
        }
        //校验token,拿到用户信息
        R<AuthAccountBO> authAccountBO = tokenFeignService.checkToken(accessToken);
        if (!authAccountBO.isSuccess()) {
            //未登录
            printServerResponseToWeb(response, R.err(ResponseEnum.ACCESS_TOKEN_OVERDUE));
            return;
        }
        //校验权限 rbac 最后做
        try {
            //保存用户信息上下文到当前线程
            AuthUserContext.set(authAccountBO.getData());
            chain.doFilter(request, response);

        } finally {
            AuthUserContext.clean();
        }

    }

    boolean feignRequestCheck(HttpServletRequest req) {
        if (req.getRequestURI().startsWith(FEIGN_INSIDE_URL_PREFIX)) {
            return true;
        }
        return false;
    }

    private <T> void printServerResponseToWeb(HttpServletResponse response, R<T> r) {
        response.setCharacterEncoding(CharsetUtil.UTF_8);
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        try {
            PrintWriter writer = response.getWriter();
            writer.write(objectMapper.writeValueAsString(r));
        } catch (IOException e) {
            throw new MallException("io异常");
        }
    }

}
